package com.bubblegumproject.ogo.tx;

import com.bubblegumproject.ogo.component.Interceptor;
import com.bubblegumproject.ogo.component.InvocationContext;
import org.apache.log4j.Logger;

import javax.persistence.EntityManager;

/**
 * Interceptor that enforces automagic transactional demarcation.
 *
 * @author Azubuko Obele (buko.obele@gmail.com)
 */
public class TransactionInterceptor implements Interceptor {

    private static final Logger log = Logger.getLogger(TransactionInterceptor.class);

    public void initialize(Object configData) {
        ;
    }

    public Object execute(InvocationContext context) throws Throwable {
        // start a transaction iff one is not currently active
        boolean startedTx = !Transaction.isActive();

        try {
            if (startedTx) {
                log.debug(this + " starting tranction because no tx is active");
                Transaction.begin();
            }

            Object result = context.proceed();

            if (startedTx) {
                // have to work with components that might've set rollback only
                EntityManager entityManager = Transaction.current();
                if (entityManager.getTransaction().getRollbackOnly()) {
                    log.debug(this + " rolling back transaction because rollbackOnly");
                    Transaction.rollback();
                } else {
                    log.debug(this + " committing transaction I started");
                    Transaction.commit();
                }
            }

            return result;
        } catch (Throwable t) {
            if (startedTx) log.debug(this + " encountered " + t, t);
            try {
                // rollback this tx iff I started it
                if (Transaction.isActive() && startedTx) {
                    log.debug(this + " rolling back current tx because of exception" + t);
                    Transaction.rollback();
                }
            } catch (Throwable rbex) {
                log.error(this + " failed to rollback current tx in " + context, rbex);
            }

            // in this case, we want to be doubly sure to remove the current tx if we started it
            if (startedTx) {
                Transaction.cleanup();
            }

            throw t;
        }
    }
}
